package Game.Objects.Creatures;

import GUI.GuiManager;
import GUI.GuiMessenger;
import Game.GameHelper;
import Game.Buff.BuffManager;
import Game.Items.GameItem;
import Game.Items.Inventory;
import Game.Objects.GOPlayer;
import Game.Objects.GameObject;
import Game.Objects.ObjectManager;
import GameMath.GPoint;
import Screens.MyScreens.GameScreen;
import Tools.Drawing.LayersHelper;
import Tools.Pathfinding.Pathfinding;

import java.lang.reflect.InvocationTargetException;
import java.util.*;

import org.newdawn.slick.SlickException;

/** Creature base class
 * @author Aikidos
 */
public class GOCreature extends GameObject {
	
	/** List of walk path point */
	private List<GPoint> _walkPath = new ArrayList<GPoint>();
	
	/** Buff manager */
	private BuffManager _buffManager;
	
	/** Inventory */
	private Inventory _inventory;
	
	/** Creature health */
	private int _health;
	
	/** Minimum damage */
	public int MinDamage;
	
	/** Maximum damage */
	public int MaxDamage;
	
	/** Maximum creature health */
	public int MaxHealth;
	
	/** Creature defense */
	public int Defense;
	
	/** Block chance */
	public int BlockChance; 
	
	/** Creature magic */
	public int Magic; 
	
	/** Give experience	*/
	public int Experience;
	
	/** Gold */
	public int Gold;
	
	public GOCreature(ObjectManager objectManager, String name) {
		super(objectManager, name);
		
		// Initialize buff manager
		_buffManager = new BuffManager(this);
		
		// Initialize inventory
		_inventory = new Inventory(this);
		_isCreature = true;
		
		setLayerForDrawing(LayersHelper.LAYER_CREATURES);
	}
	
	/** Add some health
	 * @param health - Health
	 */
	public void addHealth(int health) {
		_health += health;
		
		if (_health > MaxHealth) _health = MaxHealth;
	}
	
	/** Sub some health
	 * @param health - Health
	 */
	public void subHealth(int health) {
		_health -= health;
		
		if (_health < 0) _health = 0;
	}
	
	/** Returns health
	 * @return Health
	 */
	public int getHealth() {
		return _health;
	}

	/** Set health
	 * @param health - Health
	 */
	public void setHealth(int health) {
		_health = (health > MaxHealth) ? MaxHealth : health;
		if (_health < 0) _health = 0;
	}
	
	/** Is died? */
	public boolean isDied() {
		return (_health == 0);
	}
	
	/** Returns inventory
	 * @return Inventory of creature
	 */
	public Inventory getInventory() {
		return _inventory;
	}
	
	/** Returns buff manager
	 * @return Buff manager
	 */
	public BuffManager getBuffManager() {
		return _buffManager;
	}
	
	/** Returns walk path
	 * @return List of walk path point
	 */
	public List<GPoint> getWalkPath() {
		return _walkPath;
	}
	
	/** Create the walk path to coordinate
	 * @param p - Finish point
	 */
	public void go(GPoint p) {
		_walkPath.clear();

		List<GPoint> path = Pathfinding.find(_objectManager.getObjectCoord(this), p); 
		
		if (path != null)
		{
			_walkPath = path;
			_walkPath.remove(0);
		}
	}

	/** Walk step 
	 * @throws SlickException */
	public void walk() throws SlickException {
		if (_walkPath.size() > 0)
		{
			if (_objectManager.moveTo(this, _walkPath.get(0)))
				_walkPath.remove(0);
		}
	}
	
	@Override
	public void update(int delta) throws SlickException,
			InstantiationException, IllegalAccessException,
			IllegalArgumentException, InvocationTargetException,
			NoSuchMethodException, SecurityException, ClassNotFoundException {

		super.update(delta);
		
		_buffManager.update(delta);
	}

	@Override
	public void step() throws SlickException {
		super.step();
		
		_buffManager.step();
	}

	/** Attack game object
	 * @param gameObject - Game object
	 */
	public void attack(GameObject gameObject) {
		Random rnd = GameHelper.getRandom();
		
		if (!isDied()) {
			if (gameObject.isCreature()) {
				GOCreature creature = (GOCreature) gameObject;
				
				// Block?
				if (GameHelper.getRandom().nextInt(100) <= creature.BlockChance)
					return;

				// Call attack method for each item
				for (GameItem item : getInventory().getItems()) {
					item.attack(creature);
				}
				
				// Calculate damage
				int d = MinDamage + rnd.nextInt((MaxDamage - MinDamage) + 1) - creature.Defense;

				// If damage > 0
				if (d > 0) {
					creature.subHealth(d);

					// If creature is died give all loot
					if (creature.isDied()) {
						List<GameItem> items = creature.getInventory()
								.getItems();

						for (GameItem item : items) {
							if ((creature.getInventory().getDropChance(item) > 0) && 
									(GameHelper.getRandom().nextInt(100) <= creature.getInventory().getDropChance(item))) {
								item.off();
								getInventory().addItem(item);
							}
						}
					}
				}
			}

			if (gameObject.isPlayer()) {
				GOPlayer player = (GOPlayer) gameObject;

				// Block?
				if (GameHelper.getRandom().nextInt(100) <= player.BlockChance) {
					// Show message
					GuiManager.getMessenger().addMessage("[green]You[/green] blocked attack!");
					return;
				}
				
				// Call attack method for each item
				for (GameItem item : getInventory().getItems()) {
					item.attack(player);
				}
				
				// Calculate damage
				int d = MinDamage + rnd.nextInt((MaxDamage - MinDamage) + 1) - player.Defense;

				// If damage > 0
				if (d > 0) {
					player.subHealth(d);

					// Show message
					GuiManager.getMessenger().addMessage(
							"[red]" + this.getName() + "[/red]"
									+ " attacked [green]You[/green] (-" + String.valueOf(d)
									+ " hp)");
				}
				else {
					// Else missed
					GuiManager.getMessenger().addMessage(
							"[red]" + this.getName() + "[/red]"
									+ " missed [green]You[/green]");
				}
			}
		}
	}
}
